package com.atguigu.tingshu.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.common.util.MongoUtil;
import com.atguigu.tingshu.model.user.UserListenProcess;
import com.atguigu.tingshu.user.service.UserListenProcessService;
import com.atguigu.tingshu.vo.album.TrackStatMqVo;
import com.atguigu.tingshu.vo.user.UserListenProcessVo;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.DateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserListenProcessServiceImpl implements UserListenProcessService {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public BigDecimal getTrackBreakSecond(Long trackId) {

        Long userId = AuthContextHolder.getUserId();

        // 1. 构建条件对象
        Criteria criteria = Criteria.where("userId").is(userId).and("trackId").is(trackId);

        // 2. 构建一个查询对象
        Query query = new Query(criteria);

        // 3.查询
        String collectionName = MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_LISTEN_PROCESS, userId);

        UserListenProcess userListenProcess = mongoTemplate.findOne(query, UserListenProcess.class, collectionName);
        if (userListenProcess == null) {
            // 没播放
            return BigDecimal.ZERO;
        }
        // 播放
        return userListenProcess.getBreakSecond();
    }

    @Override
    public void updateListenProcess(UserListenProcessVo userListenProcessVo) {
        // 1.获取用户id
        Long userId = AuthContextHolder.getUserId();
        // 2.构建条件对象
        Criteria criteria = Criteria.where("userId").is(userId).and("trackId").is(userListenProcessVo.getTrackId());
        // 3.构建查询对象
        Query query = new Query(criteria);
        // 4.查询
        String collectionName = MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_LISTEN_PROCESS, userId);
        UserListenProcess userListenProcess = mongoTemplate.findOne(query, UserListenProcess.class, collectionName);
        if (userListenProcess == null) {
            // 第一次来播放 保存
            userListenProcess = new UserListenProcess();
            userListenProcess.setId(ObjectId.get().toString());
            userListenProcess.setUserId(userId);
            userListenProcess.setAlbumId(userListenProcessVo.getAlbumId());
            userListenProcess.setTrackId(userListenProcessVo.getTrackId());
            userListenProcess.setBreakSecond(userListenProcessVo.getBreakSecond());
            userListenProcess.setIsShow(1);
            userListenProcess.setCreateTime(new Date());
            userListenProcess.setUpdateTime(new Date());
            mongoTemplate.save(userListenProcess, collectionName);
            log.info("保存用户播放记录：{}", userListenProcess);
        } else {
            // 更新
            userListenProcess.setUpdateTime(new Date());
            userListenProcess.setBreakSecond(userListenProcessVo.getBreakSecond());
            mongoTemplate.save(userListenProcess, collectionName);
            log.info("更新用户播放记录：{}", userListenProcess);
        }

        // 5.修改MySQL中专辑和声音的播放量(异步发送消息给下游专辑微服务更新声音和专辑播放量)
        // 5.1 构建一个消息对象
        // 控制上游只发送一次消息
        // - 1. 分布式锁（redis中setIfAbsent/redisson中lock/mysql/zk）
        // - 2. bitMap

        String bitmapKey = new DateTime().toString("yyyyMMdd") + ":" + userListenProcessVo.getTrackId();
        // 返回的是bitMap中先前的值
        Boolean aBoolean = redisTemplate.opsForValue().setBit(bitmapKey, userId, true);
        if (!aBoolean) {
            redisTemplate.expire(bitmapKey, 1, TimeUnit.DAYS);
            // 当天当前用户第一次播放该声音
            TrackStatMqVo trackStatMqVo = prepareStatMsgDto(userListenProcessVo);
            rabbitService.sendMessage(MqConst.EXCHANGE_TRACK, MqConst.ROUTING_TRACK_STAT_UPDATE, JSONObject.toJSONString(trackStatMqVo));
        }
    }

    @Override
    public Map<String, Object> getLatelyTrack() {
        Long userId = AuthContextHolder.getUserId();
        // 构建条件对象

        Criteria criteria = Criteria.where("userId").is(userId).and("isShow").is(1);

        // 构建查询对象
        Query query = new Query(criteria);

        // 根据更新声音的修改时间找最大的
        Sort sort = Sort.by(Sort.Direction.DESC, "updateTime");

        query.with(sort);

        // 获取最新播放声音
        String collectionName = MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_LISTEN_PROCESS, userId);
        UserListenProcess userListenProcess = mongoTemplate.findOne(query, UserListenProcess.class, collectionName);
        if (userListenProcess == null){
            return null;
        }

        HashMap<String, Object> map = new HashMap<>();
        map.put("trackId", userListenProcess.getTrackId());
        map.put("albumId", userListenProcess.getAlbumId());

        return map;
    }

    private TrackStatMqVo prepareStatMsgDto(UserListenProcessVo userListenProcessVo) {
        TrackStatMqVo trackStatMqVo = new TrackStatMqVo();
        // 业务编号
        trackStatMqVo.setBusinessNo(UUID.randomUUID().toString().replace("-", ""));
        // 专辑id
        trackStatMqVo.setAlbumId(userListenProcessVo.getAlbumId());
        // 声音id
        trackStatMqVo.setTrackId(userListenProcessVo.getTrackId());
        // 统计类型
        trackStatMqVo.setStatType(SystemConstant.ALBUM_STAT_PLAY);
        // 更新数目
        trackStatMqVo.setCount(1);
        return trackStatMqVo;
    }
}
